 /*******************************************************************************
  * Copyright (c) 2004, 2006 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/

 package org.eclipse.ui.internal.intro.impl.model.loader;

 import java.util.ArrayList ;
 import java.util.Hashtable ;
 import java.util.List ;
 import java.util.Vector ;

 import org.eclipse.core.runtime.IConfigurationElement;
 import org.eclipse.core.runtime.IExtensionRegistry;
 import org.eclipse.core.runtime.Platform;
 import org.eclipse.ui.internal.intro.impl.model.AbstractIntroIdElement;
 import org.eclipse.ui.internal.intro.impl.model.IntroModelRoot;
 import org.eclipse.ui.internal.intro.impl.util.Log;
 import org.eclipse.ui.internal.intro.impl.util.Util;

 /**
  * Base class for handling Intro Extensions.
  */
 public class BaseExtensionPointManager {

     // the config extension id
 protected static final String CONFIG = "org.eclipse.ui.intro.config"; //$NON-NLS-1$

     // the configExtension extension id
 protected static final String CONFIG_EXTENSION = "org.eclipse.ui.intro.configExtension"; //$NON-NLS-1$

     // the attribute in the config element to specify the intro part id.
 protected static final String ATT_CONFIG_INTRO_ID = "introId"; //$NON-NLS-1$

     // the attribute in the config element to specify the intro part id.
 protected static final String ATT_CONFIG_EXTENSION_CONFIG_ID = "configId"; //$NON-NLS-1$

     // the id attribute in any intro element.
 protected static final String ATT_ID = AbstractIntroIdElement.ATT_ID;


     protected Hashtable introModels = new Hashtable ();
     protected IExtensionRegistry registry;
     protected SharedConfigExtensionsManager sharedConfigExtensionsManager;
     private String extensionFilter;

     /*
      * Prevent creation.
      */
     protected BaseExtensionPointManager() {
         registry = Platform.getExtensionRegistry();
     }

     protected IntroModelRoot loadModel(String attributeName,
             String attributeValue) {

         long start = 0;
         if (Log.logPerformance)
             start = System.currentTimeMillis();

         // get all Config extension point contributions. There could be more
 // than one config contribution, but there should only be one that maps
 // to the cached intro part id.
 IConfigurationElement introConfig = getIntroConfig(attributeName,
             attributeValue);

         // load model with the config elements of the correct contribution. If
 // there are no valid contribution, model stays null.
 if (introConfig != null) {
             // we found matching config. Get all configExtension contributed to
 // this config and pass to model. Load generic config extensions as
 // well.
 String configId = introConfig.getAttribute(ATT_ID);
             IConfigurationElement[] introConfigExtensions = null;
             if (configId == null)
                 // if id of config is null, pass empty array.
 introConfigExtensions = new IConfigurationElement[0];
             else
                 introConfigExtensions = getIntroConfigExtensions(
                     ATT_CONFIG_EXTENSION_CONFIG_ID, configId);

             if (Log.logPerformance)
                 Util.logPerformanceTime(
                     "BEGIN: quering registry for configs took: ", start); //$NON-NLS-1$


             IntroModelRoot model = new IntroModelRoot(introConfig,
                 introConfigExtensions);
             model.loadModel();
             // add the current model to the hash table of models, only if it is
 // not null. They key is the model id, which is the id of the
 // config that defined this model.
 addCachedModel(model.getId(), model);

             // now load all generic config extension. ie: standbyPart and
 // command contributions.
 loadSharedConfigExtensions();

             if (Log.logPerformance)
                 Util
                     .logPerformanceTime(
                         "loading Intro Model (quering registry/creating & resolving model) took: ", //$NON-NLS-1$
 start);

             return model;
         }
         return null;
     }

     /**
      * Go through all the config elements and only return the correct config
      * that maps to the correct intro part id. If there is more than one config
      * thats maps to the same intro part id, log the fact, and return the first
      * one. If there are non, return null.
      *
      * @param configElements
      * @return
      */
     protected IConfigurationElement getIntroConfig(String attrributeName,
             String attributeValue) {

         IConfigurationElement[] configElements = registry
             .getConfigurationElementsFor(CONFIG);

         IConfigurationElement config = getConfigurationFromAttribute(
             configElements, attrributeName, attributeValue);

         if (config == null)
             // if there is no valid config, log the fact.
 Log.warning("No Intro configuration found with " + attrributeName //$NON-NLS-1$
 + " of value = " + attributeValue); //$NON-NLS-1$

         return config;
     }

     /**
      * Go through all the configExtension elements and return an array of all
      * extensions that match the attribute and its value. If there are non,
      * return empty array. This also loads all standby contributions.
      */
     protected IConfigurationElement[] getIntroConfigExtensions(
             String attrributeName, String attributeValue) {

         IConfigurationElement[] configExtensionElements = registry
             .getConfigurationElementsFor(CONFIG_EXTENSION);
         
         /*
          * Extension filter is used for performance testing to only load contributions
          * from a specific plug-in (fixed data set).
          */
         if (extensionFilter != null) {
             List filtered = new ArrayList ();
             for (int i=0;i<configExtensionElements.length;++i) {
                 if (extensionFilter.equals(configExtensionElements[i].getContributor().getName())) {
                     filtered.add(configExtensionElements[i]);
                 }
             }
             configExtensionElements = (IConfigurationElement[])filtered.toArray(new IConfigurationElement[filtered.size()]);
         }

         IConfigurationElement[] configExtensions = getConfigurationsFromAttribute(
             configExtensionElements, attrributeName, attributeValue);

         return configExtensions;
     }

     /**
      * Add a model to the cache. This method is private because only this
      * manager class knows how to load an intro model.
      *
      * @param modelId
      * @param model
      */
     protected void addCachedModel(String modelId, IntroModelRoot model) {
         introModels.put(modelId, model);
     }

     /**
      * Gets the given model from the cache.
      *
      * @param modelId
      */
     protected IntroModelRoot getCachedModel(String configId) {
         return (IntroModelRoot) introModels.get(configId);
     }

     /**
      * Go through all the config elements and only return the correct config
      * with an attribute of the given value. If there is more than one
      * configuration element that maps to the attribute value log the fact, and
      * return the first one. If there are non, return null.
      *
      * @param configElements
      * @return
      */
     protected IConfigurationElement getConfigurationFromAttribute(
             IConfigurationElement[] configElements, String attributeName,
             String attributeValue) {

         // find all configs with given attribute and attibute value.
 IConfigurationElement[] filteredConfigElements = getConfigurationsFromAttribute(
             configElements, attributeName, attributeValue);
         // now validate that we got only one.
 IConfigurationElement config = ModelLoaderUtil
             .validateSingleContribution(filteredConfigElements, attributeName);
         return config;
     }

     /**
      * Go through all the config elements and return an array of matching
      * configs with an attribute of the given value. If there are non, return
      * empty array.
      */
     protected IConfigurationElement[] getConfigurationsFromAttribute(
             IConfigurationElement[] configElements, String attributeName,
             String attributeValue) {

         // find all configs with given attribute and attibute value.
 Vector elements = new Vector ();
         for (int i = 0; i < configElements.length; i++) {
             String currentAttributeValue = configElements[i]
                 .getAttribute(attributeName);
             if (currentAttributeValue != null
                     && currentAttributeValue.equals(attributeValue))
                 elements.add(configElements[i]);
         }

         // now return array.
 IConfigurationElement[] filteredConfigElements = new IConfigurationElement[elements
             .size()];
         elements.copyInto(filteredConfigElements);

         return filteredConfigElements;
     }

     /**
      * Loads all shared config extennsions (ie: standby parts and commands).
      */
     protected void loadSharedConfigExtensions() {
         sharedConfigExtensionsManager = new SharedConfigExtensionsManager(
             registry);
         sharedConfigExtensionsManager.loadSharedConfigExtensions();
     }


     /**
      * @return Returns the sharedConfigExtensionsManager.
      */
     public SharedConfigExtensionsManager getSharedConfigExtensionsManager() {
         return sharedConfigExtensionsManager;
     }

     /**
      * @return Returns the cached introModels.
      */
     public Hashtable getIntroModels() {
         return introModels;
     }
     
     /*
      * Internal test hook for restricting which extensions to load.
      */
     public void setExtensionFilter(String pluginId) {
         extensionFilter = pluginId;
     }
 }

